﻿#include "AutoPatcher.AMain.h"
#include "Settings.h"
#include "Properties/Resources.Designer.h"

namespace AutoPatcher
{

    AMain::AMain()
    {
        InitializeComponent();
    }

    void AMain::Start()
    {
        OldList = std::vector<FileInformation*>();
        DownloadList = std::deque<FileInformation*>();

        std::vector<unsigned char> data = Download(Settings::PatchFileName);

        if (!data.empty())
        {
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (MemoryStream stream = new MemoryStream(data))
            {
                MemoryStream stream = MemoryStream(data);
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (BinaryReader reader = new BinaryReader(stream))
                {
                BinaryReader reader = BinaryReader(stream);
                ParseOld(reader);
                }
            }
        }
        else
        {
            MessageBox::Show("Could not get Patch Information.");
            Completed = true;
            return;
        }

        _fileCount = OldList.size();
        for (int i = 0; i < OldList.size(); i++)
        {
            CheckFile(OldList[i]);
        }

        Checked = true;
        _fileCount = 0;
        _currentCount = 0;


        _fileCount = DownloadList.size();
        BeginDownload();
    }

    void AMain::BeginDownload()
    {
        if (DownloadList.empty())
        {
            return;
        }

        if (DownloadList.empty())
        {
            DownloadList = std::deque<FileInformation*>();
            _currentFile = nullptr;
            Completed = true;

            CleanUp();
            return;
        }

        _currentFile = DownloadList.pop_front();

        Download(_currentFile);
    }

    void AMain::CleanUp()
    {
        if (!Settings::AllowCleanUp)
        {
            return;
        }

        std::vector<std::string> fileNames;

        if (FileSystem::directoryExists(R"(.\Data\)"))
        {
            fileNames = Directory::GetFiles(R"(.\Data\)", R"(*.lib)");

            for (int i = 0; i < fileNames.size(); i++)
            {
                if (!NeedFile(fileNames[i]))
                {
                    File::Delete(fileNames[i]);
                }
            }
        }


        if (FileSystem::directoryExists(R"(.\Sound\)"))
        {
            fileNames = Directory::GetFiles(R"(.\Sound\)", R"(*.wav)");

            for (int i = 0; i < fileNames.size(); i++)
            {
                if (!NeedFile(fileNames[i]))
                {
                    File::Delete(fileNames[i]);
                }
            }

            fileNames = Directory::GetFiles(R"(.\Sound\)", R"(*.mp3)");

            for (int i = 0; i < fileNames.size(); i++)
            {
                if (!NeedFile(fileNames[i]))
                {
                    File::Delete(fileNames[i]);
                }
            }
        }

        if (FileSystem::directoryExists(R"(.\Map\)"))
        {
            fileNames = Directory::GetFiles(R"(.\Map\)", R"(*.map)");

            for (int i = 0; i < fileNames.size(); i++)
            {
                if (!NeedFile(fileNames[i]))
                {
                    File::Delete(fileNames[i]);
                }
            }
        }

    }

    bool AMain::NeedFile(const std::string &fileName)
    {
        for (int i = 0; i < OldList.size(); i++)
        {
            if (StringHelper::endsWith(fileName, OldList[i]->FileName))
            {
                return true;
            }
        }

        return false;
    }

    void AMain::ParseOld(BinaryReader *reader)
    {
        int count = reader->ReadInt32();

        for (int i = 0; i < count; i++)
        {
            FileInformation tempVar(reader);
            OldList.push_back(&tempVar);
        }
    }

    void AMain::CheckFile(FileInformation *old)
    {
        FileInformation *info = GetFileInformation(Settings::Client + (old->FileName == "AutoPatcher.gz" ? "AutoPatcher.exe" : old->FileName));
        _currentCount++;

        if (info == nullptr || old->Length != info->Length || old->Creation != info->Creation)
        {
            DownloadList.push_back(old);
            _totalBytes += old->Compressed;
        }
    }

    void AMain::Download(FileInformation *info)
    {
        std::string fileName = StringHelper::replace(info->FileName, R"(\)", "/");

        if (fileName != "PList.gz")
        {
            fileName += Path::GetExtension(fileName);
        }

        try
        {
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (WebClient client = new WebClient())
            {
                WebClient client = WebClient();
                client.DownloadProgressChanged += [&] (o, e)
                {
                    _currentBytes = e::BytesReceived;
                };
                client.DownloadDataCompleted += [&] (o, e)
                {
                    if (e::Error != nullptr)
                    {
                        File::AppendAllText(R"(.\Error.txt)", StringHelper::formatSimple("[{0}] {1}{2}", DateTime::Now, info->FileName + " could not be downloaded. (" + e::Error->Message + ")", "\r\n"));
                        ErrorFound = true;
                    }
                    else
                    {
                        _currentCount++;
                        _completedBytes += _currentBytes;
                        _currentBytes = 0;
                        _stopwatch->Stop();
    
                        if (!FileSystem::directoryExists(Settings::Client + FileSystem::getDirectoryName(info->FileName)))
                        {
                            FileSystem::createDirectory(Settings::Client + FileSystem::getDirectoryName(info->FileName));
                        }
    
                        File::WriteAllBytes(Settings::Client + info->FileName, Decompress(e::Result));
                        File::SetLastWriteTime(Settings::Client + info->FileName, info->Creation);
                    }
                    BeginDownload();
                };

                if (Settings::NeedLogin)
                {
                    client.Credentials = new NetworkCredential(Settings::Login, Settings::Password);
                }


                _stopwatch = Stopwatch::StartNew();
                Uri tempVar(Settings::Host + Path::ChangeExtension("/" + fileName, ".gz"));
                client.DownloadDataAsync(&tempVar);
            }
        }
        catch (...)
        {
            MessageBox::Show(StringHelper::formatSimple("Failed to download file: {0}", fileName));
        }
    }

    std::vector<unsigned char> AMain::Download(const std::string &fileName)
    {
        fileName = StringHelper::replace(fileName, R"(\)", "/");

        if (fileName != "AutoPatcher.gz" && fileName != "PList.gz")
        {
            fileName += Path::GetExtension(fileName);
        }

        try
        {
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (WebClient client = new WebClient())
            {
                WebClient client = WebClient();
                if (Settings::NeedLogin)
                {
                    client.Credentials = new NetworkCredential(Settings::Login, Settings::Password);
                }
                else
                {
                    client.Credentials = new NetworkCredential("", "");
                }

                return Decompress(client.DownloadData(Settings::Host + Path::ChangeExtension(fileName, ".gz")));
            }
        }
        catch (...)
        {
            return std::vector<unsigned char>();
        }
    }

    std::vector<unsigned char> AMain::Decompress(std::vector<unsigned char> &raw)
    {
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (GZipStream gStream = new GZipStream(new MemoryStream(raw), CompressionMode.Decompress))
        {
            MemoryStream tempVar(raw);
            GZipStream gStream = GZipStream(&tempVar, CompressionMode::Decompress);
            constexpr int size = 4096; //4kb
            std::vector<unsigned char> buffer(size);
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (MemoryStream mStream = new MemoryStream())
            {
                MemoryStream mStream = MemoryStream();
                int count;
                do
                {
                    count = gStream.Read(buffer, 0, size);
                    if (count > 0)
                    {
                        mStream.Write(buffer, 0, count);
                    }
                } while (count > 0);
                return mStream.ToArray();
            }
        }
    }

    std::vector<unsigned char> AMain::Compress(std::vector<unsigned char> &raw)
    {
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (MemoryStream mStream = new MemoryStream())
        {
            MemoryStream mStream = MemoryStream();
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (GZipStream gStream = new GZipStream(mStream, CompressionMode.Compress, true))
            {
                GZipStream gStream = GZipStream(mStream, CompressionMode::Compress, true);
                gStream.Write(raw, 0, raw.size());
            }
            return mStream.ToArray();
        }
    }

    FileInformation *AMain::GetFileInformation(const std::string &fileName)
    {
        if (!FileSystem::fileExists(fileName))
        {
            return nullptr;
        }

        FileInfo *info = new FileInfo(fileName);

        delete info;
        FileInformation *tempVar = new FileInformation();
//C# TO C++ CONVERTER TODO TASK: There is no C++ equivalent to the .NET String 'Remove' method unless assigning to the string instance:
        tempVar->FileName = fileName.Remove(0, Settings::Client.length());
        tempVar->Length = static_cast<int>(info->Length);
        tempVar->Creation = info->LastWriteTime;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was used in a 'return' or 'throw' statement.
        return tempVar;
    }

    void AMain::ImageLinkLabel_LinkClicked(std::any sender, LinkLabelLinkClickedEventArgs *e)
    {
        ImageLinkLabel->LinkVisited = true;
        Process::Start("http://www.lomcn.org/forum/member.php?5330-DevilsKnight");
    }

    void AMain::AMain_Load(std::any sender, EventArgs *e)
    {
        PlayButton->Enabled = false;
        _workThread = new Thread([&] () {Start();});
        _workThread->IsBackground = true;
        _workThread->Start();
    }

    void AMain::InterfaceTimer_Tick(std::any sender, EventArgs *e)
    {
        try
        {
            if (Completed)
            {
                if (Settings::AutoStart)
                {
                    Play();
                    return;
                }
                ActionLabel->Text = "Completed.";
                SizeLabel->Text = "Completed.";
                FileLabel->Text = "Completed.";
                SpeedLabel->Text = "Completed.";
                progressBar1->Value = 100;
                progressBar2->Value = 100;
                PlayButton->Enabled = true;
                InterfaceTimer->Enabled = false;

                if (ErrorFound)
                {
                    MessageBox::Show("One or more files failed to download, check Error.txt for details.", "Failed to Download.");
                }
                ErrorFound = false;
                return;
            }

            ActionLabel->Text = !Checked ? StringHelper::formatSimple("Checking Files... {0}/{1}", _currentCount, _fileCount) : StringHelper::formatSimple("Downloading... {0}/{1}", _currentCount, _fileCount);
            SizeLabel->Text = std::string::Format("{0:#,##0} MB / {1:#,##0} MB", (_completedBytes + _currentBytes) / 1024 / 1024, _totalBytes / 1024 / 1024);

            if (_currentFile != nullptr)
            {
                FileLabel->Text = std::string::Format("{0}, ({1:#,##0} MB) / ({2:#,##0} MB)", _currentFile->FileName, _currentBytes / 1024 / 1024, _currentFile->Compressed / 1024 / 1024);
//C# TO C++ CONVERTER TODO TASK: There is no C++ equivalent to 'ToString':
                SpeedLabel->Text = (_currentBytes / 1024.0F / _stopwatch->Elapsed.TotalSeconds).ToString("#,##0.##") + "KB/s";
                progressBar2->Value = static_cast<int>(100 * _currentBytes / _currentFile->Compressed);
            }
            progressBar1->Value = static_cast<int>(100 * (_completedBytes + _currentBytes) / _totalBytes);
        }
        catch (...)

        {

        }

    }

    void AMain::CancelButton_Click(std::any sender, EventArgs *e)
    {
        Close();
    }

    void AMain::PlayButton_Click(std::any sender, EventArgs *e)
    {
        Play();
    }

    void AMain::Play()
    {
        if (FileSystem::fileExists(Settings::Client + "Client.exe"))
        {
            ProcessStartInfo *info = new ProcessStartInfo(Settings::Client + "Client.exe");
            info->WorkingDirectory = Settings::Client;
            Process::Start(info);

//C# TO C++ CONVERTER TODO TASK: A 'delete info' statement was not added since info was passed to a method or constructor. Handle memory management manually.
        }
        else
        {
            MessageBox::Show(Settings::Client + "Client.exe not found.", "Client not found.");
        }
        Close();

    }

    void AMain::SourceLinkLabel_LinkClicked(std::any sender, LinkLabelLinkClickedEventArgs *e)
    {
        SourceLinkLabel->LinkVisited = true;
        Process::Start("http://www.lomcn.org/forum/member.php?141-Jamie-Hello");
    }

    void AMain::Dispose(bool disposing)
    {
        if (disposing && (components != nullptr))
        {
            components->Dispose();
        }
        System::Windows::Forms::Form::Dispose(disposing);
    }

    void AMain::InitializeComponent()
    {
        this->components = new System::ComponentModel::Container();
        this->pictureBox1 = new System::Windows::Forms::PictureBox();
        this->ImageLinkLabel = new System::Windows::Forms::LinkLabel();
        this->label1 = new System::Windows::Forms::Label();
        this->ActionLabel = new System::Windows::Forms::Label();
        this->progressBar1 = new System::Windows::Forms::ProgressBar();
        this->CancelButton = new System::Windows::Forms::Button();
        this->PlayButton = new System::Windows::Forms::Button();
        this->SizeLabel = new System::Windows::Forms::Label();
        this->label3 = new System::Windows::Forms::Label();
        this->FileLabel = new System::Windows::Forms::Label();
        this->label5 = new System::Windows::Forms::Label();
        this->SpeedLabel = new System::Windows::Forms::Label();
        this->label7 = new System::Windows::Forms::Label();
        this->progressBar2 = new System::Windows::Forms::ProgressBar();
        this->InterfaceTimer = new System::Windows::Forms::Timer(this->components);
        this->SourceLinkLabel = new System::Windows::Forms::LinkLabel();
        (static_cast<System::ComponentModel::ISupportInitialize*>(this->pictureBox1))->BeginInit();
        this->SuspendLayout();
        // 
        // pictureBox1
        // 
        this->pictureBox1->Image = ::AutoPatcher::Properties->Resources::getCMir();
        this->pictureBox1->Location = new System::Drawing::Point(12, 12);
        this->pictureBox1->Name = "pictureBox1";
        this->pictureBox1->Size = new System::Drawing::Size(501, 152);
        this->pictureBox1->TabIndex = 0;
        this->pictureBox1->TabStop = false;
        // 
        // ImageLinkLabel
        // 
        this->ImageLinkLabel->AutoSize = true;
        this->ImageLinkLabel->Location = new System::Drawing::Point(401, 167);
        this->ImageLinkLabel->Name = "ImageLinkLabel";
        this->ImageLinkLabel->Size = new System::Drawing::Size(112, 13);
        this->ImageLinkLabel->TabIndex = 2;
        this->ImageLinkLabel->TabStop = true;
        this->ImageLinkLabel->Text = "Image by DevilsKnight";
        this->ImageLinkLabel->LinkClicked += new System::Windows::Forms::LinkLabelLinkClickedEventHandler(this->ImageLinkLabel_LinkClicked);
        // 
        // label1
        // 
        this->label1->AutoSize = true;
        this->label1->Location = new System::Drawing::Point(35, 177);
        this->label1->Name = "label1";
        this->label1->Size = new System::Drawing::Size(40, 13);
        this->label1->TabIndex = 3;
        this->label1->Text = "Action:";
        // 
        // ActionLabel
        // 
        this->ActionLabel->AutoSize = true;
        this->ActionLabel->Location = new System::Drawing::Point(81, 177);
        this->ActionLabel->Name = "ActionLabel";
        this->ActionLabel->Size = new System::Drawing::Size(85, 13);
        this->ActionLabel->TabIndex = 4;
        this->ActionLabel->Text = "Checking Files...";
        // 
        // progressBar1
        // 
        this->progressBar1->Location = new System::Drawing::Point(12, 219);
        this->progressBar1->Name = "progressBar1";
        this->progressBar1->Size = new System::Drawing::Size(501, 17);
        this->progressBar1->TabIndex = 5;
        // 
        // CancelButton
        // 
        this->CancelButton->Location = new System::Drawing::Point(438, 258);
        this->CancelButton->Name = "CancelButton";
        this->CancelButton->Size = new System::Drawing::Size(75, 23);
        this->CancelButton->TabIndex = 6;
        this->CancelButton->Text = "Cancel";
        this->CancelButton->UseVisualStyleBackColor = true;
        this->CancelButton->Click += new System::EventHandler(this->CancelButton_Click);
        // 
        // PlayButton
        // 
        this->PlayButton->Location = new System::Drawing::Point(12, 258);
        this->PlayButton->Name = "PlayButton";
        this->PlayButton->Size = new System::Drawing::Size(75, 23);
        this->PlayButton->TabIndex = 7;
        this->PlayButton->Text = "Play";
        this->PlayButton->UseVisualStyleBackColor = true;
        this->PlayButton->Click += new System::EventHandler(this->PlayButton_Click);
        // 
        // SizeLabel
        // 
        this->SizeLabel->AutoSize = true;
        this->SizeLabel->Location = new System::Drawing::Point(81, 190);
        this->SizeLabel->Name = "SizeLabel";
        this->SizeLabel->Size = new System::Drawing::Size(24, 13);
        this->SizeLabel->TabIndex = 9;
        this->SizeLabel->Text = "Idle";
        // 
        // label3
        // 
        this->label3->AutoSize = true;
        this->label3->Location = new System::Drawing::Point(18, 190);
        this->label3->Name = "label3";
        this->label3->Size = new System::Drawing::Size(57, 13);
        this->label3->TabIndex = 8;
        this->label3->Text = "Total Size:";
        // 
        // FileLabel
        // 
        this->FileLabel->AutoSize = true;
        this->FileLabel->Location = new System::Drawing::Point(81, 203);
        this->FileLabel->Name = "FileLabel";
        this->FileLabel->Size = new System::Drawing::Size(24, 13);
        this->FileLabel->TabIndex = 11;
        this->FileLabel->Text = "Idle";
        // 
        // label5
        // 
        this->label5->AutoSize = true;
        this->label5->Location = new System::Drawing::Point(12, 203);
        this->label5->Name = "label5";
        this->label5->Size = new System::Drawing::Size(63, 13);
        this->label5->TabIndex = 10;
        this->label5->Text = "Current File:";
        // 
        // SpeedLabel
        // 
        this->SpeedLabel->AutoSize = true;
        this->SpeedLabel->Location = new System::Drawing::Point(448, 203);
        this->SpeedLabel->Name = "SpeedLabel";
        this->SpeedLabel->Size = new System::Drawing::Size(24, 13);
        this->SpeedLabel->TabIndex = 13;
        this->SpeedLabel->Text = "Idle";
        // 
        // label7
        // 
        this->label7->AutoSize = true;
        this->label7->Location = new System::Drawing::Point(401, 203);
        this->label7->Name = "label7";
        this->label7->Size = new System::Drawing::Size(41, 13);
        this->label7->TabIndex = 12;
        this->label7->Text = "Speed:";
        // 
        // progressBar2
        // 
        this->progressBar2->Location = new System::Drawing::Point(12, 242);
        this->progressBar2->Name = "progressBar2";
        this->progressBar2->Size = new System::Drawing::Size(501, 10);
        this->progressBar2->TabIndex = 14;
        // 
        // InterfaceTimer
        // 
        this->InterfaceTimer->Enabled = true;
        this->InterfaceTimer->Interval = 50;
        this->InterfaceTimer->Tick += new System::EventHandler(this->InterfaceTimer_Tick);
        // 
        // SourceLinkLabel
        // 
        this->SourceLinkLabel->AutoSize = true;
        this->SourceLinkLabel->Location = new System::Drawing::Point(401, 284);
        this->SourceLinkLabel->Name = "SourceLinkLabel";
        this->SourceLinkLabel->Size = new System::Drawing::Size(117, 13);
        this->SourceLinkLabel->TabIndex = 15;
        this->SourceLinkLabel->TabStop = true;
        this->SourceLinkLabel->Text = "Created by Jamie/Hello";
        this->SourceLinkLabel->LinkClicked += new System::Windows::Forms::LinkLabelLinkClickedEventHandler(this->SourceLinkLabel_LinkClicked);
        // 
        // AMain
        // 
        this->AutoScaleDimensions = new System::Drawing::SizeF(6.0F, 13.0F);
        this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
        this->ClientSize = new System::Drawing::Size(525, 307);
        this->Controls->Add(this->SourceLinkLabel);
        this->Controls->Add(this->progressBar2);
        this->Controls->Add(this->SpeedLabel);
        this->Controls->Add(this->label7);
        this->Controls->Add(this->FileLabel);
        this->Controls->Add(this->label5);
        this->Controls->Add(this->SizeLabel);
        this->Controls->Add(this->label3);
        this->Controls->Add(this->PlayButton);
        this->Controls->Add(this->CancelButton);
        this->Controls->Add(this->progressBar1);
        this->Controls->Add(this->ActionLabel);
        this->Controls->Add(this->label1);
        this->Controls->Add(this->ImageLinkLabel);
        this->Controls->Add(this->pictureBox1);
        this->Name = "AMain";
        this->Text = "Auto Patcher";
        this->Load += new System::EventHandler(this->AMain_Load);
        (static_cast<System::ComponentModel::ISupportInitialize*>(this->pictureBox1))->EndInit();
        this->ResumeLayout(false);
        this->PerformLayout();

    }
}
